import {
  OnInit,
  Component,
  ViewChild,
  TemplateRef,
  Input,
  Output,
  EventEmitter,
  OnDestroy,
  OnChanges,
} from '@angular/core'
import { Observable, of, Subject } from 'rxjs'
import { switchMap, map } from 'rxjs/operators'
import { NzMessageService } from 'ng-zorro-antd'
import { ACLService } from '@delon/acl'
import { DetailHeaderComponent } from '../detail-header/detail-header.component'
import { IDetailHeaderTab, Country, Currency, StatusTextMap } from '@shared/interfaces/common'
import { IMCC } from '../mcc-select/mcc-select.interface'
import { DetailBaseClass } from '@shared/classes/DetailBaseClass'
import { Store, StoreUpdate, EOtherDetailTab, StoreCommonUpdate, StoreFull } from '@shared/interfaces/store'
import { PageService } from '@shared/services/page.service'
import {
  storeNameMaxLength,
  storeNameENMaxLength,
  storeNameENReg,
  contactNameMaxLength,
  emailMaxLength,
  emailReg,
  cityMaxLength,
  alphanumericAndSymbolsReg,
  detailAddressMaxLength,
  usernameMaxLength,
  storePostalCodeReg,
} from 'app/utils/contraints'
import { AcquirerService } from 'app/routes/acquirer/acquirer.service'
import { AcquirerAlinterService } from 'app/routes/acquirer/acquirer-alinter.service'
import { checkObjectEqual, getObjectValue } from 'app/utils/helpers'
import { ETabScrollStatus } from '@shared/enums'
import { ModalService } from '@shared/services/modal.service'
import { StoreAlinterService } from 'app/routes/store/store-alinter.service'
import { StoreService } from 'app/routes/store/store.service'
import { Validators } from 'app/utils/validator'
import { QrChannel } from '@shared/interfaces/acquirer'
import { DataService } from '@shared/services/data.service'
import { I18NService } from '@core'
import { Contact } from '@shared/interfaces/contact'
import { Address } from '@shared/interfaces/address'
import { ETaskStatus } from '@shared/interfaces/task'
import { IGeneralResponse } from '@shared/interfaces/response'

@Component({
  selector: 'app-store-detail-com',
  templateUrl: './store-detail.component.html',
  providers: [
    { provide: AcquirerService, useClass: AcquirerAlinterService },
    { provide: StoreService, useClass: StoreAlinterService },
  ],
})
export class StoreDetailComComponent extends DetailBaseClass<Store> implements OnInit, OnDestroy, OnChanges {
  _data: Store

  @Input() isNeedDiff = false // 是否需要使用diff
  @Input() origData: Store
  @Input() qrChannels: QrChannel[]
  @Input() origQrChannels: QrChannel[]
  @Input() data$: Observable<Store>
  @Input() isEditable = true
  @Input() token: string
  @Output() downloadEvent: Subject<void> = new Subject()
  @Output() freezeEvent: Subject<void> = new Subject()
  @Output() unfreezeEvent: Subject<void> = new Subject()
  @Output() cancelEvent = new EventEmitter()
  @Output() saveEvent = new EventEmitter()
  @Output() changeOpenEvent = new EventEmitter()

  @Input() set data(val: Store) {
    if (val.common.currencies === undefined) {
      val.common.currencies = new Array<Currency>()
    }
    if (val.common.contact === undefined) {
      val.common.contact = new Contact()
    }
    if (val.common.address === undefined) {
      val.common.address = new Address()
    }
    if (val.common.mccEnName === undefined) {
      val.common.mccEnName = ''
    }
    this._data = val
  }

  get data(): Store {
    return this._data
  }

  @ViewChild('tplNation') tplNation: TemplateRef<any>
  @ViewChild('tplCurrency') tplCurrency: TemplateRef<any>
  @ViewChild('tplMCC') tplMCC: TemplateRef<any>
  @ViewChild('tpControl') tpControl: TemplateRef<any>

  @ViewChild(DetailHeaderComponent) detailHeaderComponent: DetailHeaderComponent

  imgSrc = 'assets/images/centaur/base-info-pic/store.png'

  tabs$: Observable<IDetailHeaderTab[]>

  tabs: IDetailHeaderTab[] = [
    { key: EOtherDetailTab.Terminal, text: this.trans('MENU.BIZ.TERMINALS'), url: './terminals' },
    { key: EOtherDetailTab.QrChannel, text: this.trans('BIZ.ACQUIRER.OTHER.QR_CHANNELS'), url: './channels/qr' },
    { key: EOtherDetailTab.CardChannel, text: this.trans('BIZ.ACQUIRER.OTHER.CARD_CHANNELS'), url: './channels/card' },
    { key: EOtherDetailTab.EcChannel, text: this.trans('BIZ.ACQUIRER.OTHER.EC_CHANNELS'), url: './channels/ec' },
    { key: EOtherDetailTab.System, text: this.trans('GENERAL.INFO.OTHER.SYSTEM_INFO'), url: './system' },
    { key: EOtherDetailTab.Settlement, text: this.trans('GENERAL.INFO.OTHER.SETTLEMENT'), url: './settlement' },
    // { key: EOtherDetailTab.QrFunction, text: this.trans('GENERAL.INFO.OTHER.QR_FEATURES'), url: './function/qr' },
    // { key: EOtherDetailTab.CardFunction, text: '刷卡功能配置', url: './function/card' },
    { key: EOtherDetailTab.RiskControl, text: this.trans('GENERAL.INFO.OTHER.RISK_CONTROL'), url: './rc' },
    { key: EOtherDetailTab.Users, text: this.trans('GENERAL.INFO.OTHER.USERS'), url: './users' },
  ]

  selectedCountry: Country
  selectedMCC: IMCC
  /**
   * 机构支持多币种的话，这里就为true
   * 目前一期先做成只支持单币种
   */
  isCurrencyMultiple = false

  get selectedCurrency(): Currency | Currency[] {
    const { currencies } = this.detailData.common
    if (this.isCurrencyMultiple && currencies.length) {
      return this.detailData.common.currencies[0]
    }

    return currencies
  }

  set selectedCurrency(value: Currency | Currency[]) {
    if (Array.isArray(value)) {
      this.detailData.common.currencies = value
      return
    }

    const currencies = this.detailData.common.currencies || []
    if (!currencies.length) {
      currencies.push(value)
      return
    }

    currencies[0] = value
  }

  constructor(
    private msg: NzMessageService,
    public pageService: PageService,
    public aclService: ACLService,
    private acquirerService: AcquirerService,
    private modalService: ModalService,
    private storeService: StoreService,
    private dataService: DataService,
    private i18n: I18NService,
  ) {
    super(pageService)
  }

  ngOnInit() {
    this.setData()
    this.initialColumns()
    this.initColumns()
    this.genTabs()
  }

  ngOnChanges() {
    this.detailData = this.data
    this.initialColumns()
    this.initColumns()
  }

  ngOnDestroy() {
    this.resetData()
    this.pageService.triggerBaseEdit(false)
    this.pageService.setTabScrollStatus(ETabScrollStatus.InitialLoad)
  }

  genTabs(): void {
    const { insCode } = this.detailData.common
    this.tabs$ = this.acquirerService.getOne(insCode).pipe(
      map(acqFull => {
        return acqFull.institution.common.feature
      }),
      switchMap(feature => {
        // const ok = feature.isSupportRisk || feature.isSupportPostRisk
        /** 隐藏事中风控功能 */
        const ok = feature.isSupportPostRisk
        return of(ok ? this.tabs : this.tabs.filter(tab => tab.key !== EOtherDetailTab.RiskControl))
      }),
      switchMap(feature => {
        /** 隐藏用户列表功能 */
        if (this.dataService.task.status) {
          return of(
            this.dataService.task.status === ETaskStatus.Approved ? this.tabs : this.tabs.filter(tab => tab.key !== EOtherDetailTab.Users ))
        }
        return of(this.tabs)
      }),
    )
  }

  parseTabsWithDiffCheck(tabs: IDetailHeaderTab[]): Observable<IDetailHeaderTab[]> {
    if (!this.isNeedDiff) return of(tabs)
    /** if need diff, then hide terminal, system, user */
    tabs = this.removeTab(tabs, EOtherDetailTab.Terminal)
    tabs = this.removeTab(tabs, EOtherDetailTab.System)
    tabs = this.removeTab(tabs, EOtherDetailTab.Users)

    /** check if settlement is updated or not, if do, hide it */
    if (!this.checkIfQrChannelUpdated()) {
      tabs = this.removeTab(tabs, EOtherDetailTab.QrChannel)
    }

    /** check if settlement is updated or not, if do, hide it */
    if (!this.checkIfSettleUpdated()) {
      tabs = this.removeTab(tabs, EOtherDetailTab.Settlement)
    }

    /** check if qrChannel is updated or not, if do, hide it */
    if (!this.checkIfQrFunctionUpdated()) {
      tabs = this.removeTab(tabs, EOtherDetailTab.QrFunction)
    }

    /** check if riskControl is updated or not, if do, hide it */
    if (!this.checkIfRiskControlUpdated()) {
      tabs = this.removeTab(tabs, EOtherDetailTab.RiskControl)
    }

    return of(tabs)
  }

  checkIfQrChannelUpdated(): boolean {
    return !checkObjectEqual(this.origQrChannels, this.qrChannels)
  }

  checkIfSettleUpdated(): boolean {
    return !checkObjectEqual(this.origData.common.settlement, this.data.common.settlement)
  }

  checkIfQrFunctionUpdated(): boolean {
    return !checkObjectEqual(this.origData.qr, this.data.qr)
  }

  checkIfRiskControlUpdated(): boolean {
    return !checkObjectEqual(this.origData.common.riskControl, this.data.common.riskControl)
  }

  removeTab(tabs: IDetailHeaderTab[], key: string): IDetailHeaderTab[] {
    return tabs.filter(tab => tab.key !== key)
  }

  /** 判断新旧两个值是否不同，如果diff，则返回 true */
  checkDiff(index: string): boolean {
    /** 如果不需要Diff，直接返回false */
    if (!this.isNeedDiff) return false
    const origValue = getObjectValue(index, this.origData)
    const currentValue = getObjectValue(index, this.data)
    /** 如果是数组的情况, 当前，币种是数组 */
    if (origValue instanceof Array && currentValue instanceof Array) {
      return !checkObjectEqual(origValue, currentValue)
    }
    return origValue !== currentValue
  }

  initialColumns() {
    this.firstColumns = [
      {
        span: 6,
        index: 'common.storeNum',
        label: 'NUM',
        labelI18n: 'GENERAL.STORE.NUM',
        value: '',
        disEditable: true,
      },
      {
        span: 8,
        index: 'common.storeName',
        label: 'NAME',
        labelI18n: 'GENERAL.STORE.NAME',
        value: '',
        validator: {
          funcs: [Validators.required, Validators.maxLength(storeNameMaxLength)],
          errorTips: [
            this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.STORE.NAME') }),
            this.trans('VALIDATION.MAX_LENGTH', { length: storeNameMaxLength }),
          ],
        },
      },
      {
        span: 10,
        index: 'common.storeNameEN',
        label: 'NAME_EN',
        labelI18n: 'GENERAL.STORE.NAME_EN',
        value: '',
        validator: {
          funcs: [Validators.required, Validators.maxLength(storeNameENMaxLength), Validators.pattern(storeNameENReg)],
          errorTips: [
            this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.STORE.NAME_EN') }),
            this.trans('VALIDATION.MAX_LENGTH', { length: storeNameENMaxLength }),
            this.trans('VALIDATION.PATTERN.ALPHANUMERIC_SYMBOLS_ONLY'),
          ],
        },
      },
    ]
    this.secondColumns = [
      {
        span: 6,
        index: 'common.status',
        label: 'STATUS',
        labelI18n: 'GENERAL.INFO.STATUS',
        value: '',
        disEditable: true,
        displayValue: this.getStatus(),
      },
      {
        span: 8,
        index: 'common.mcc',
        label: 'MCC',
        value: '',
        pipe: () => this.parseMCC(this.detailData.common.mcc, this.detailData.common.mccEnName),
        template: this.tplMCC,
      },
      {
        span: 10,
        index: 'common.currencies',
        label: 'Currency',
        labelI18n: 'GENERAL.INFO.TRANSACTION_CURRENCY',
        pipe: () => this.parseCurValue(this.detailData.common.currencies),
        template: this.tplCurrency,
      },
    ]

    this.bottomColumnsRows = [
      [
        {
          span: 5,
          index: 'common.contact.name',
          label: 'GE NERAL.INFO.CONTACT_NAME',
          labelI18n: 'GENERAL.INFO.CONTACT_NAME',
          value: '',
          validator: {
            funcs: [Validators.required, Validators.maxLength(contactNameMaxLength)],
            errorTips: [
              this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.CONTACT_NAME') }),
              this.trans('VALIDATION.MAX_LENGTH', { length: contactNameMaxLength }),
            ],
          },
        },
        {
          span: 5,
          index: 'common.contact.email',
          label: 'GENERAL.INFO.EMAIL',
          labelI18n: 'GENERAL.INFO.EMAIL',
          value: '',
          validator: {
            funcs: [Validators.required, Validators.maxLength(emailMaxLength), Validators.pattern(emailReg)],
            errorTips: [
              this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.EMAIL') }),
              this.trans('VALIDATION.MAX_LENGTH', { length: emailMaxLength }),
              this.trans('VALIDATION.PATTERN.EMAIL'),
            ],
          },
        },
        {
          span: 5,
          index: 'common.address.nation',
          label: 'GENERAL.INFO.COUNTRY_REGION',
          labelI18n: 'GENERAL.INFO.COUNTRY_REGION',
          template: this.tplNation,
          color: '#DB2E2E',
        },
        {
          span: 5,
          index: 'common.address.city',
          label: 'GENERAL.INFO.CITY',
          labelI18n: 'GENERAL.INFO.CITY',
          validator: {
            funcs: [
              Validators.required,
              Validators.maxLength(cityMaxLength),
              Validators.pattern(alphanumericAndSymbolsReg),
            ],
            errorTips: [
              this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.CITY') }),
              this.trans('VALIDATION.MAX_LENGTH', { length: cityMaxLength }),
              this.trans('VALIDATION.PATTERN.ENGLISH_STATEMENT'),
            ],
          },
        },
        {
          span: 4,
          index: 'common.address.zipCode',
          label: 'CODE',
          labelI18n: 'GENERAL.INFO.POSTAL_CODE',
          value: '',
          validator: {
            funcs: [
              Validators.required,
              Validators.maxLength(usernameMaxLength),
              Validators.pattern(storePostalCodeReg),
            ],
            errorTips: [
              this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.POSTAL_CODE') }),
              this.trans('VALIDATION.MAX_LENGTH', { length: usernameMaxLength }),
              this.trans('VALIDATION.PATTERN.NUMBER_ONLY', { name: this.trans('GENERAL.INFO.POSTAL_CODE') }),
            ],
          },
        },
      ],
      [
        {
          span: 17,
          index: 'common.address.address',
          label: 'GENERAL.INFO.ADDRESS',
          labelI18n: 'GENERAL.INFO.ADDRESS',
          value: '',
          validator: {
            funcs: [Validators.required, Validators.maxLength(detailAddressMaxLength)],
            errorTips: [
              this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.ADDRESS') }),
              this.trans('VALIDATION.MAX_LENGTH', { length: detailAddressMaxLength }),
            ],
          },
        },
        {
          span: 7,
          label: 'GENERAL.INFO.OPERATION',
          labelI18n: 'GENERAL.INFO.OPERATION',
          template: this.tpControl,
          operate: true,
          acl: ['api.put.camel.store.one.freeze', 'api.put.camel.store.one.unfreeze', 'api.get.camel.stores.report'],
        },
      ],
    ]

    if (!this.isEditable) {
      this.bottomColumnsRows.pop()
      this.bottomColumnsRows.push([
        {
          span: 24,
          index: 'common.address.address',
          label: '地址',
          labelI18n: 'GENERAL.INFO.ADDRESS',
          value: '',
          validator: {
            funcs: [Validators.required, Validators.maxLength(detailAddressMaxLength)],
            errorTips: [
              this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.ADDRESS') }),
              this.trans('VALIDATION.MAX_LENGTH', { length: detailAddressMaxLength }),
            ],
          },
        },
      ])
    }
  }

  transCountriesToOptions(countries: Country[]) {
    return countries.map(country => {
      return { label: country.name, value: country.codeAlpha3 }
    })
  }

  freezeDialog(): void {
    const { storeName } = this.detailData.common
    const msg = this.trans('GENERAL.NOTIFICATION.STORE_FREEZE_CONFIRM', { storeName })
    this.modalService.freezeAndUnFreeze(msg).subscribe(isOK => {
      if (!isOK) return
      this.freeze()
    })
  }

  unfreezeDialog(): void {
    const { storeName } = this.detailData.common
    const msg = this.trans('GENERAL.NOTIFICATION.STORE_UNFREEZE_CONFIRM', { storeName })
    this.modalService.freezeAndUnFreeze(msg).subscribe(isOK => {
      if (!isOK) return
      this.unfreeze()
    })
  }

  download(): void {
    this.downloadEvent.next()
  }

  freeze(): void {
    this.freezeEvent.next()
  }

  unfreeze(): void {
    this.unfreezeEvent.next()
  }

  triggerBaseEdit(): void {
    this.changeOpenEvent.emit()
    this.copiedDetailData = this.copyDetailData(this.detailData)
    this.pageService.triggerBaseEdit(true)
    this.renderDefaultValueInSelect(this.detailData)
  }

  renderDefaultValueInSelect(store: Store): void {
    const country = new Country()
    country.codeAlpha3 = store.common.address.nation
    country.name = store.common.address.nation
    this.selectedCountry = country

    this.selectedMCC = {
      mccCode: store.common.mcc,
      mccEnName: store.common.mccEnName,
    }
  }

  cancelBase(): void {
    this.cancelEvent.emit(this.copiedDetailData)
    // this.renderStore(this.copiedDetailData)
    this.clearColumnsErrorTips()
    this.initColumns()
    this.pageService.triggerBaseEdit(false)
  }

  saveBase(): void {
    if (!this.checkEditValidation()) {
      this.msg.error(this.trans('GENERAL.NOTIFICATION.VALIDATION_FAIL'))
      return
    }
    try {
      this.detailHeaderComponent.hideButtons()
      this.pageService.triggerBaseEdit(false)
      this.updateColumns()
      // 把参数整理出来
      const update = new StoreUpdate(this.detailData.version)
      update.common.storeName = this.detailData.common.storeName
      update.common.storeNameEN = this.detailData.common.storeNameEN
      update.common.contact.name = this.detailData.common.contact.name
      update.common.contact.email = this.detailData.common.contact.email
      update.common.currencies = this.detailData.common.currencies
      update.common.status = this.detailData.common.status
      if (this.selectedMCC) {
        update.common.mcc = this.selectedMCC.mccCode
      }
      /** 后端全量保存，要将所有的数据都返回过去 */
      Object.assign(update.common.address, this.detailData.common.address)
      /** 国家是通过 selectCountry 来取值，之后要优化 TODO: */
      if (this.selectedCountry) {
        update.common.address.nation = this.selectedCountry.codeAlpha3
      }
      const { intStoreCode } = this.detailData.common

      this.saveEvent.emit({ intStoreCode, update })
      this.storeService.update(intStoreCode, this.token, update).subscribe((res) => this.renderAfterUpdate(res, update))
    } catch (err) {
      this.msg.error(this.trans('GENERAL.NOTIFICATION.UPDATE_FAIL'), err.message)
    }
  }

  renderAfterUpdate(res: StoreFull | IGeneralResponse, update: StoreUpdate): void {
    if (res instanceof StoreFull) {
      /** 直接更新成功 */
      const { store } = res
      const { common, version } = store
      Object.assign(this.detailData.common, common)
      this.detailData.version = version
      this.initColumns()
      this.clearColumnsErrorTips()
      return
    }

    /** 需要进审核 */
    if (this.pageService.isStoreNeedAudit) {
      Object.assign(this.detailData.common, update.common, { mccEnName: this.selectedMCC.mccEnName })
      this.initColumns()
      this.clearColumnsErrorTips()
      this.msg.success(this.trans('GENERAL.INFO.SUBMIT_AFTER_SAVING'))
      this.pageService.triggerShake(true)
      this.pageService.setStoreBeenModified(true)
      setTimeout(() => {
        this.pageService.triggerShake(false) // 2s 之后就关掉它
      }, 2000)
    }
  }

  currencyRequiredHandler(): void {
    const msg = this.trans('VALIDATION.REQUIRED', { name: this.trans('GENERAL.INFO.TRANSACTION_CURRENCY') })
    this.msg.error(msg)
  }

  private setData(): void {
    this.detailData = this.data
    this.dataService.setCurrentStore(this.data)
    this.dataService.setOrigStore(this.origData)
    this.dataService.setIsNeedDiff(this.isNeedDiff)
  }

  private resetData(): void {
    this.dataService.resetOrigStore()
    this.dataService.resetCurrentStore()
    this.dataService.resetIsNeedDiff()
  }

  private parseCurValue(currencies: Array<Currency> = []): string {
    return currencies.map(c => c.code).join('、')
  }

  private parseMCC(mcc, mccEnName): string {
    return mcc + ' ' + mccEnName
  }

  private trans(key: string, params?: Object): string {
    return this.i18n.fanyi(key, params)
  }

  private getStatus(): string {
    return this.trans(StatusTextMap[this.detailData.common.status])
  }
}
